#ifndef VERTEX_H
#define VERTEX_H

// Vertex Class
#include <vector>
#include <set>
#include <list>
#include <string>
#include "VertexBase.h"
#include "VertexDirectory.h"

using namespace std;

class Graph;
class Group;
typedef vector<bool> VertexKey;
class VertexDirectory;
class VertexSet;

class Vertex : public VertexBase
{
public:
	Vertex(VertexKey vertexName, VertexDirectory* dir = NULL);
	~Vertex();
	// copies vertexToCopy's data INTO this instance
	void Copy(Vertex* vertexToCopy);
	// Properties
	const VertexKey Name();
	VertexContainer* GetContainer();
	VertexContainer* GetPreviousContainer();
	void ResetContainer();
	long Rank();
	void UpdateRank(long modifier);
	bool operator==(Vertex &otherVertex);
	bool operator!=(Vertex &otherVertex);
	bool IsInGraph();
	
	//Methods
	bool Neighbours(VertexBase* otherVertex);
	bool NeighboursInGraph(VertexBase* otherVertex);
	bool NeighboursNotInGraph(VertexBase* otherVertex);
	void MoveToContainer(VertexContainer* otherContainer, bool vertexInGraph, bool updateNeighbours);
	void MoveToContainer(VertexContainer* otherContainer, bool vertexInGraph);

	set<VertexKey> GetReplacements();
	set<VertexKey> GetInterchangeables();
	set<Vertex *> GetInterchangeableVertices();

	bool Interchangeable();
	bool IsReplacement();

	void SetInterchangeableFlag(bool value) { m_Interchangeable = value; }
	void SetReplacementFlag(bool value) { m_IsReplacement = value; }
	

	// Checks if this vertex is interchangeable, creates the replacement list and adds vertex to verticesToReplace lists
	void FindReplacements(Graph *graph = NULL); 
	// Set flags, adds other vertex to replacement list
	void SetInterchangeable(VertexBase* otherVertex, bool updateContainer);
	// removes other vertex from replacement list
	void ResetInterchangeableWithVertex(Vertex* otherVertex);
	// Marks this vertex is not interchangeable - clears flags and m_replacements, removes it from all m_Vertices to replace
	void ResetInterchangeable();
	// Adds vertex to m_VerticesToReplace list
	void SetAsReplacementFor(VertexBase* otherVertex);
	// Removes vertex from m_VerticesToReplace list
	void ResetReplacementForVertex(Vertex* otherVertex);
	// Clears flags, clears m_VerticesToReplace, removes from all m_replacements lists
	void ResetIsReplacement();
		
	bool CanBeReplaced();
	bool CanReturnToOriginalGroup();
	bool CanBeRemovedFromReplacements();
	bool CanAllRelatedInterchangeablesBeReplaced();
	bool CanAllVerticesToReplaceBeReplaced();

	void UpdateReplacementState();
	void UpdateReplacementStateForAllNeighboursInGraph();
	
	bool HasNeighbourInGroup(Group *group);

	void Replace(Graph *graph);
	void ReturnToOriginalContainer(bool updateLists = true);
	void RemoveAllReplacements();
	void UndoReplacementWith(Vertex *otherVertex, bool returnToGraph);
	void ReplaceWith(Vertex *replacementVertex, Graph *graph, bool updateLists);
	Vertex *GetLegalReplacementVertex();

	void ClearInterchangeableInformation();

	void RemoveNeighbour(Vertex* neighbour, bool silentMode = false);
	void AddNeighbour(Vertex* neighbour, bool silentMode = false);
	string PrintName();
	set<VertexKey> GetNeighboursInGraph();
	set<VertexKey> GetNeighboursNotInGraph();
	static VertexKey GetNameFromArray(bool* ar, int n);
	int GetCombinedRank(Vertex* other);
	VertexDirectory* GetVertexDirectory();
	Graph *GetContainingGraph();

	VertexSet* GetContainingVertexSet();
	void SetContaingVertexSet(VertexSet *vertexSet);

private:
	//private methods
	void CreateNeighbours();
	bool ValidNeighbour(VertexKey otherVertex);
	void UpdateNeighbours(bool inGraph, set<VertexKey > SetToUpdated);
	int CalcWeight(VertexKey name);

	Vertex *GetVertexFromRightDirectory(VertexKey name);

	set<Vertex*> GetAllRelatedInterchangeables();
	bool CanWholeListBeReplaced(set<Vertex*> interchangeables);

	//Vertex Data
	VertexKey m_VertexName;
	long m_Rank;
	
	// true iff this vertex is still in the graph
	bool m_InGraph;
	// if m_Interchangeable == true, this is a list of the vertices this vertex can be replaced with
	set<VertexKey> m_Replacements;
	// if m_Replacement == true, this is a list of the vertices this vertex can replace
	set<VertexKey> m_VerticesToReplace;
	// A link to the vertex this vertex will replace
	Vertex* m_VertexThisReplaces; 
	// the group this vertex is a member of right now
	VertexContainer* m_CurrentContainer;
	// if this vertex is currently interchangable, then this is a pointer to the group the vertex was a member of, before
	// being marked as interchangable;
	VertexContainer* m_InterchangableContainer;
	set<VertexKey > m_NeighboursInGraph; // set of the vertex's neighbours that are still in the graph
	set<VertexKey > m_NeighboursNotInGraph; // swt of the vertex's neighbours that are not in the graph (e.g. in some group)
	int m_VertexWeight; //# of 1's in the vertex name
	// the directory in which this vertex will look for its neighbours. If NULL, will look in the static directory
	VertexDirectory* m_Directory; 

	VertexSet *m_ContainingVertexSet;
};

#endif